From d74d94ce69b1637390b5268f904a8832a8e88ddd Mon Sep 17 00:00:00 2001 From: Alexander Larsson Date: Tue, 9 Dec 2008 20:13:02 +0100 Subject: [PATCH] Implement flicker free handling of window moves/resizes --- gdk/gdkwindow.c | 236 ++++++++++++++++++++++++++++++++++++++---------- gdk/gdkwindow.h | 2 + 2 files changed, 191 insertions(+), 47 deletions(-) diff --git a/gdk/gdkwindow.c b/gdk/gdkwindow.c index 43723cc050..a561c2d0cb 100644 --- a/gdk/gdkwindow.c +++ b/gdk/gdkwindow.c @@ -72,6 +72,12 @@ struct _GdkWindowPaint guint32 region_tag; }; +typedef struct { + GdkRegion *region; /* The destination region */ + int dx, dy; +} GdkWindowRegionMove; + + /* Global info */ static GdkGC *gdk_window_create_gc (GdkDrawable *drawable, @@ -230,6 +236,12 @@ static void remove_redirect_from_children (GdkWindowObject *private, static void recompute_visible_regions (GdkWindowObject *private, gboolean recalculate_siblings, gboolean recalculate_children); +static void gdk_window_flush (GdkWindow *window); +static void do_move_region_bits_on_impl (GdkWindowObject *private, + GdkDrawable *dest, + int dest_off_x, int dest_off_y, + GdkRegion *region, /* In impl window coords */ + int dx, int dy); static gpointer parent_class = NULL; @@ -1743,6 +1755,9 @@ gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect) { GdkWindowObject *private = (GdkWindowObject *)window; GdkWindowPaint *paint; + GdkRectangle r, clipbox; + GList *l; + GdkWindowRegionMove *move; g_assert (gdk_window_has_impl (private)); @@ -1753,15 +1768,43 @@ gdk_window_begin_implicit_paint (GdkWindow *window, GdkRectangle *rect) private->implicit_paint != NULL) return FALSE; /* Don't stack implicit paints */ + r = *rect; + for (l = private->outstanding_moves; l != NULL; l = l->next) + { + move = l->data; + + gdk_region_get_clipbox (move->region, &clipbox); + gdk_rectangle_union (&r, &clipbox, &r); + } + paint = g_new (GdkWindowPaint, 1); paint->region = gdk_region_new (); /* Empty */ - paint->x_offset = rect->x; - paint->y_offset = rect->y; + paint->x_offset = r.x; + paint->y_offset = r.y; paint->uses_implicit = FALSE; paint->surface = NULL; paint->pixmap = gdk_pixmap_new (window, - MAX (rect->width, 1), MAX (rect->height, 1), -1); + MAX (r.width, 1), MAX (r.height, 1), -1); + + _gdk_pixmap_set_as_backing (paint->pixmap, + window, r.x, r.y); + + for (l = private->outstanding_moves; l != NULL; l = l->next) + { + move = l->data; + + gdk_region_union (paint->region, move->region); + g_object_ref (paint->pixmap); + do_move_region_bits_on_impl (private, + paint->pixmap, + paint->x_offset, paint->y_offset, + move->region, /* In impl window coords */ + move->dx, move->dy); + gdk_region_destroy (move->region); + g_slice_free (GdkWindowRegionMove, move); + } + private->outstanding_moves = NULL; private->implicit_paint = paint; @@ -2201,12 +2244,155 @@ gdk_window_free_paint_stack (GdkWindow *window) } } +static void +do_move_region_bits_on_impl (GdkWindowObject *private, + GdkDrawable *dest, + int dest_off_x, int dest_off_y, + GdkRegion *region, /* In impl window coords */ + int dx, int dy) +{ + GdkGC *tmp_gc; + GdkRectangle copy_rect; + + gdk_region_get_clipbox (region, ©_rect); + gdk_region_offset (region, -dest_off_x, -dest_off_y); + tmp_gc = _gdk_drawable_get_scratch_gc ((GdkWindow *)private, TRUE); + gdk_gc_set_clip_region (tmp_gc, region); + gdk_draw_drawable (dest, + tmp_gc, + private->impl, + copy_rect.x-dx, copy_rect.y-dy, + copy_rect.x - dest_off_x, copy_rect.y - dest_off_y, + copy_rect.width, copy_rect.height); + gdk_gc_set_clip_region (tmp_gc, NULL); +} + +static void +append_move_region (GdkWindowObject *impl_window, + GdkRegion *region, + int dx, int dy) +{ + GList *moves_to_add, *l, *s; + GdkRegion *intersection; + GdkWindowRegionMove *move, *new_move, *existing_move; + + move = g_slice_new (GdkWindowRegionMove); + move->region = region; + move->dx = dx; + move->dy = dy; + + moves_to_add = g_list_prepend (NULL, move); + + for (l = impl_window->outstanding_moves; l != NULL; l = l->next) + { + existing_move = l->data; + + for (s = moves_to_add; s != NULL; s = s->next) + { + move = s->data; + + intersection = gdk_region_copy (move->region); + gdk_region_offset (intersection, -move->dx, -move->dy); + gdk_region_intersect (intersection, existing_move->region); + gdk_region_offset (intersection, move->dx, move->dy); + + if (!gdk_region_empty (intersection)) + { + + new_move = g_slice_new (GdkWindowRegionMove); + new_move->region = intersection; + new_move->dx = move->dx + existing_move->dx; + new_move->dy = move->dy + existing_move->dy; + moves_to_add = g_list_prepend (moves_to_add, new_move); + + gdk_region_subtract (move->region, intersection); + gdk_region_subtract (existing_move->region, intersection); + } + else + gdk_region_destroy (intersection); + } + } + + impl_window->outstanding_moves = g_list_concat (impl_window->outstanding_moves, + moves_to_add); + +} + +/* Moves bits and update area by dx/dy in impl window + * Takes ownership of region. + */ +static void +move_region_on_impl (GdkWindowObject *private, + GdkRegion *region, /* In impl window coords */ + int dx, int dy) +{ + GdkWindowObject *impl_window; + gboolean free_region; + + free_region = TRUE; + impl_window = gdk_window_get_impl_window (private); + + if (1) /* Enable flicker free handling of moves. */ + { + free_region = FALSE; + + append_move_region (impl_window, region, dx, dy); + } + else + do_move_region_bits_on_impl (private, + private->impl, 0,0, + region, dx, dy); + + /* Move any old invalid regions in the copy source area by dx/dy */ + if (impl_window->update_area) + { + GdkRegion *update_area; + + update_area = gdk_region_copy (region); + /* Convert from target to source */ + gdk_region_offset (update_area, -dx, -dy); + gdk_region_intersect (update_area, impl_window->update_area); + gdk_region_subtract (impl_window->update_area, update_area); + /* Convert back */ + gdk_region_offset (update_area, dx, dy); + gdk_region_union (impl_window->update_area, update_area); + gdk_region_destroy (update_area); + } + + if (free_region) + gdk_region_destroy (region); +} + /* Flushes all outstanding changes to the window, call this * before drawing directly to the window (i.e. outside a begin/end_paint pair). */ static void gdk_window_flush (GdkWindow *window) { + GdkWindowObject *private; + GdkWindowObject *impl_window; + GList *l; + GdkWindowRegionMove *move; + + private = (GdkWindowObject *) window; + + impl_window = gdk_window_get_impl_window (private); + + for (l = impl_window->outstanding_moves; l != NULL; l = l->next) + { + move = l->data; + + do_move_region_bits_on_impl (private, + private->impl, 0, 0, + move->region, move->dx, move->dy); + + gdk_region_destroy (move->region); + g_slice_free (GdkWindowRegionMove, move); + } + + g_list_free (impl_window->outstanding_moves); + impl_window->outstanding_moves = NULL; + gdk_window_flush_implicit_paint (window); } @@ -5249,50 +5435,6 @@ move_native_children (GdkWindowObject *private) } } -/* Moves bits and update area by dx/dy in impl window - * Takes ownership of region. - */ -static void -move_region_on_impl (GdkWindowObject *private, - GdkRegion *region, /* In impl window coords */ - int dx, int dy) -{ - GdkGC *tmp_gc; - GdkRectangle copy_rect; - GdkWindowObject *impl_window; - - gdk_region_get_clipbox (region, ©_rect); - tmp_gc = _gdk_drawable_get_scratch_gc ((GdkWindow *)private, TRUE); - gdk_gc_set_clip_region (tmp_gc, region); - gdk_draw_drawable (private->impl, - tmp_gc, - private->impl, - copy_rect.x-dx, copy_rect.y-dy, - copy_rect.x, copy_rect.y, - copy_rect.width, copy_rect.height); - gdk_gc_set_clip_region (tmp_gc, NULL); - - impl_window = gdk_window_get_impl_window (private); - - /* Move any old invalid regions in the copy source area by dx/dy */ - if (impl_window->update_area) - { - GdkRegion *update_area; - - update_area = gdk_region_copy (region); - /* Convert from target to source */ - gdk_region_offset (update_area, -dx, -dy); - gdk_region_intersect (update_area, impl_window->update_area); - gdk_region_subtract (impl_window->update_area, update_area); - /* Convert back */ - gdk_region_offset (update_area, dx, dy); - gdk_region_union (impl_window->update_area, update_area); - gdk_region_destroy (update_area); - } - - gdk_region_destroy (region); -} - static void gdk_window_move_resize_internal (GdkWindow *window, gboolean with_move, diff --git a/gdk/gdkwindow.h b/gdk/gdkwindow.h index 5b18ad2a08..5c475785f0 100644 --- a/gdk/gdkwindow.h +++ b/gdk/gdkwindow.h @@ -335,6 +335,8 @@ struct _GdkWindowObject gint8 toplevel_window_type; GdkWindowPaint *implicit_paint; + + GList *outstanding_moves; cairo_surface_t *cairo_surface; }; -- 2.30.2